#include "config.h"

#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#include <signal.h>
#include <time.h>

#define DBG_SUBSYS S_LIBSTORAGE

#include "sysy_lib.h"
#include "cache.h"
#include "get_version.h"
#include "vnode.h"
#include "cluster.h"
#include "volume.h"
#include "lichstor.h"
#include "stor_root.h"
#include "lich_md.h"
#include "md_map.h"
#include "configure.h"
#include "job_dock.h"
#include "chunk.h"
#include "system.h"
#include "ynet_rpc.h"
#include "etcd.h"
#include "net_global.h"
#include "ylog.h"
#include "dbg.h"
#include "lichstor.h"
#include "schedule.h"
#include "timer.h"

#define __CREATETIME__ "createtime"

#define SET_VERSION_PATH        SHM_ROOT"/sysctl/setversion"

static worker_handler_t system_handler;

int system_init()
{
        return 0;
}

STATIC int __system_set_createtime_worker(void *_args)
{
        int ret;

        (void) _args;
        ret = system_get_createtime(NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        DERROR("system set createtime error ret (%d) %s\n", ret, strerror(ret));

        ret = timer1_settime(&system_handler, USEC_PER_MIN);
        if (unlikely(ret)) {
                DERROR("settime error ret %d\n", ret);
                YASSERT(0);
        }

        return ret;
}

int system_set_createtime()
{
        int ret;

        ret = timer1_create(&system_handler, "system_set_createtime", __system_set_createtime_worker, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = timer1_settime(&system_handler, USEC_PER_MIN);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int system_get_createtime(time_t *createtime)
{
        int ret, buflen;
        time_t now;

        buflen = sizeof(now);
retry:
        ret = etcd_get_bin(ETCD_MISC, __CREATETIME__, &now, &buflen, NULL);
        if (unlikely(ret)) {
                if (ret == ENOKEY) {
                        now = gettime();
                        ret = etcd_create(ETCD_MISC, __CREATETIME__, &now, sizeof(now), 0);
                        if (unlikely(ret)) {
                                if (ret == EEXIST) {
                                        DWARN("ctime exit\n");
                                        goto retry;
                                } else
                                        GOTO(err_ret, ret);
                        }
                } else
                        GOTO(err_ret, ret);
        }

        if (createtime)
                *createtime = now;

        return 0;
err_ret:
        return ret;
}

/*
  for(i = 0; i < node->num_node; i++){
  print_etcd_node((etcd_node_t*)node->nodes[i]);
  }
*/

int system_pool_iterator(func_int1_t func, void *arg)
{
        int ret, i;
        etcd_node_t *list = NULL, *node;

        ret = etcd_list(ETCD_STORAGE, &list);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        for(i = 0; i < list->num_node; i++){
                node = list->nodes[i];
                ret = func(node->key, arg);
                if (unlikely(ret))
                        GOTO(err_free, ret);
        }

        free_etcd_node(list);

        return 0;
err_free:
        free_etcd_node(list);
err_ret:
        return ret;
}

int system_pool_iterator1(func_int1_t func, void *arg)
{
        int ret, i, retry = 0;
        etcd_node_t *list = NULL, *node;

        ret = etcd_list(ETCD_STORAGE, &list);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        for(i = 0; i < list->num_node; i++){
                node = list->nodes[i];
retry:
                ret = func(node->key, arg);
                if (unlikely(ret)) {
                        if (ret == EAGAIN) {
                                USLEEP_RETRY(err_continue, ret, retry, retry, 30, (100 * 1000));
err_continue:
                                continue;
                        } else if (ret == ENOSPC) {
                                continue;
                        } else {
                                GOTO(err_free, ret);
                        }
                }
        }

        free_etcd_node(list);
        
        return 0;
err_free:
        free_etcd_node(list);
err_ret:
        return ret;
}

int system_pool_add(const char *name)
{
        int ret, retry = 0;
        chkinfo_t chkinfo;

        if (strchr(name, '/')) {
                ret = EINVAL;
                GOTO(err_ret, ret);
        }

retry:
        memset(&chkinfo, 0x0, sizeof(chkinfo_t));
        ret = etcd_create(ETCD_STORAGE, name, &chkinfo,
                          CHKINFO_SIZE(chkinfo.repnum), 0);
        if (unlikely(ret)) {
                if (ret == EAGAIN && retry == 0) {
                        retry = 1;
                        goto retry;
                } else
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int system_pool_del(const char *name)
{
        return etcd_del(ETCD_STORAGE, name);
}

typedef struct {
        const char *pool;
        int find;
} arg_t;

static int __system_pool_find(void *_name, void *_arg)
{
        char *name = _name;
        arg_t *arg = _arg;

        if (!strcmp(name, arg->pool)) {
                arg->find = 1;
        }

        return 0;
}

int system_pool_find(const char *pool, int *_find)
{
        int ret;
        arg_t arg;

        arg.pool = pool;
        arg.find = 0;

        ret = system_pool_iterator(__system_pool_find, &arg);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        *_find = arg.find;

        return 0;
err_ret:
        return ret;
}

int system_pool_count(int *count)
{
        int ret;
        etcd_node_t *list = NULL;

        ret = etcd_list(ETCD_STORAGE, &list);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        *count = list->num_node;

        free_etcd_node(list);

        return 0;
err_ret:
        return ret;
}

static int __add_pool_to_list(void *arg1, void *arg2)
{
        int ret;
        pool_name_t *pool_name;

        const char *pool = arg1;
        struct list_head *list = arg2;

        ret = ymalloc((void **)&pool_name, sizeof(pool_name_t));
        if (unlikely(ret))
                GOTO(err_ret, ret);

        strcpy(pool_name->pool, pool);
        list_add_tail(&pool_name->hook, list);

        return 0;
err_ret:
        return ret;
}

int system_pool_list(struct list_head *list)
{
        int ret;

        INIT_LIST_HEAD(list);

        ret = system_pool_iterator(__add_pool_to_list, list);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int system_pool_list_exists(struct list_head *list, const char *name) {
        int found = 0;
        struct list_head *pos, *n;
        pool_name_t *pool_name;

        list_for_each_safe(pos, n, list) {
                pool_name = list_entry(pos, pool_name_t, hook);
                if (strcmp(name, pool_name->pool) == 0) {
                        found = 1;
                        break;
                }
        }

        return found;
}

int system_get_pool_info(char *name, void *chkinfo)
{
        int ret, retry = 0;

retry:
        ret = etcd_get_bin(ETCD_STORAGE, name,
                        chkinfo, NULL, NULL);
        if (unlikely(ret)) {
                if (ret == EAGAIN || ret == ENONET || ret == ENOSYS) {
                        USLEEP_RETRY(err_ret, ret, retry, retry, 5, (100 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int etcd_task_iterator(const char *name, func_int1_t func, void *arg)
{
        int ret, i;
        etcd_node_t *list = NULL, *node;
        char path[MAX_PATH_LEN];

        sprintf(path, "task/%s", name);

        ret = etcd_list(path, &list);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        for(i = 0; i < list->num_node; i++){
                node = list->nodes[i];
                ret = func(node->key, arg);
                if (unlikely(ret)) {
                        DWARN("key %s ret %d\n", node->key, ret);
                        // GOTO(err_free, ret);
                }
        }

        free_etcd_node(list);

        return 0;
// err_free:
//         free_etcd_node(list);
err_ret:
        return ret;
}
